package com.huijie.app.u.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.huijie.app.configuration.RedisConfiguration;
import com.huijie.app.u.entity.Token;
import com.huijie.app.u.mapper.TokenMapper;
import com.huijie.app.u.service.ITokenService;
import com.huijie.app.sys.aspect.SystemServiceLog;
import com.huijie.app.util.RedisUtil;
import com.huijie.app.util.TokenUtil;
import com.huijie.core.common.BaseServiceImpl;
import com.huijie.core.util.ThreadPool;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.util.Date;

/**
 * <p>
 * Token表 服务实现类
 * </p>
 *
 * @author echen
 * @since 2019-01-09
 */
@Service
public class TokenServiceImpl extends BaseServiceImpl<TokenMapper, Token> implements ITokenService {

    /**
     *
     * @param request 请求
     * @param id 令牌ID
     * @param subject 用户ID
     * @param issuer 签发人
     * @param period 有效时间(毫秒)
     * @param roles 访问主张-角色
     * @param permissions 访问主张-权限
     * @param algorithm 加密算法
     * @param userId
     * @param userType
     * @return json web token
     */
    @Override
    @SystemServiceLog(description = "签发令牌并保存信息")
    public String issueJwt(HttpServletRequest request, String id, String subject, String issuer, Long period, String roles
            , String permissions, SignatureAlgorithm algorithm, Integer userId, int userType) {
        long currentTimeMillis = System.currentTimeMillis();// 当前时间戳
        byte[] secretKeyBytes = DatatypeConverter.parseBase64Binary(TokenUtil.SECRET_KEY);// 秘钥
        JwtBuilder jwt = Jwts.builder();
        Token token = new Token();
        //tokenId
        if (Strings.isNotBlank(id)) {
            jwt.setId(id);
            token.setTokenId(id);
        }
        // 用户名主题
        jwt.setSubject(subject);
        token.setAppId(subject);
        token.setUserName(subject);
        //签发者
        if (Strings.isNotBlank(issuer)) {
            jwt.setIssuer(issuer);
            token.setIssuer(issuer);
        }
        //签发时间
        Date issuedAt = new Date(currentTimeMillis);
        jwt.setIssuedAt(issuedAt);
        token.setIssuedAt(issuedAt);
        //角色
        if (Strings.isNotBlank(roles)) {
//        	jwt.claim("roles", roles);
            token.setRoles(roles);
        }
        //权限
        if (Strings.isNotBlank(permissions)) {
//        	jwt.claim("perms", permissions);
            token.setPerms(permissions);
        }
        jwt.compressWith(CompressionCodecs.DEFLATE);//压缩，可选GZIP
        jwt.signWith(algorithm, secretKeyBytes);//加密设置

        //Token对象写入缓存/数据库
        long dateL = null != period ? currentTimeMillis + period : currentTimeMillis + TokenUtil.getJwtPeriod();
        //有效时间
//        jwt.setExpiration(new Date(dateL));
        token.setExpiration(new Date(dateL));
        token.setUserId(userId);
        token.setUserType(userType);
        this.create(token);
        request.setAttribute(TokenUtil.TOKEN_ATTRITUBE, token);
        //缓存到redis(tokenId+分隔符+appId+固定值->key)
        String key = token.getTokenId() + RedisConfiguration.CUT + token.getAppId() + RedisConfiguration.TOKEN;
        RedisUtil.setex(key, JSON.toJSONString(token), (TokenUtil.getJwtPeriod().intValue() / 1000));

        return jwt.compact();
    }

    /**
     *插入
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @SystemServiceLog(description = "插入令牌信息Service")
    public boolean create(Token token) {
        log.debug("插入令牌信息开始");
        long start = System.currentTimeMillis();
        try {
            if (baseMapper.insert(token)==0) {
                return false;
            }
        } catch (Exception e) {
            log.error("插入令牌信息方法内部错误", e);
            throw e;
        } finally {
            log.debug("插入令牌信息结束,用时" + (System.currentTimeMillis() - start) + "毫秒");
        }
        return true;
    }


    /**
     *物理删除 TODO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @SystemServiceLog(description = "删除令牌信息Service")
    public boolean deleteHard(Token token) {
        long start = System.currentTimeMillis();
        ThreadPool.getPool().execute(new Runnable() {
            @Override
            @Transactional(rollbackFor = Exception.class)
            public void run() {
                log.debug("删除令牌信息线程开始");
                try {
                    baseMapper.deleteById(token.getId());
                } catch (Exception e) {
                    log.error("删除令牌信息线程方法内部错误", e);
                    throw e;
                } finally {
                    log.debug("删除令牌信息线程结束,用时" + (System.currentTimeMillis() - start) + "毫秒");
                }
            }
        });
        return true;
    }

    /**
     *更新
     */
    @Override
    @SystemServiceLog(description = "更新令牌信息Service")
    public boolean modify(Token token) {
        long start = System.currentTimeMillis();
        ThreadPool.getPool().execute(new Runnable() {
            @Override
            @Transactional(rollbackFor = Exception.class)
            public void run() {
                log.debug("更新令牌信息线程开始");
                try {
                    if (null != token.getId()) {
                        baseMapper.updateById(token);
                    } else {
                        LambdaUpdateWrapper<Token> updateWrapper = new UpdateWrapper<Token>().lambda().eq(Token::getTokenId, token.getTokenId());
                        baseMapper.update(token,updateWrapper);
                    }
                } catch (Exception e) {
                    log.error("更新令牌信息线程方法内部错误", e);
                    throw e;
                } finally {
                    log.debug("更新令牌信息线程结束,用时" + (System.currentTimeMillis() - start) + "毫秒");
                }
            }
        });
        return true;
    }

    /**
     * @param token
     * @return boolean
     * @description: 同步更新令牌信息Service
     * @author zyc
     * @date 2018/12/21 10:05
     */
    @Override
    @SystemServiceLog(description = "同步更新令牌信息Service")
    public boolean modifySync(Token token) {
        Integer integer;
        if (null != token.getId()) {
            integer = baseMapper.updateById(token);
        } else {
            LambdaUpdateWrapper<Token> updateWrapper = new UpdateWrapper<Token>().lambda().eq(Token::getTokenId, token.getTokenId());
            integer = baseMapper.update(token,updateWrapper);
        }
        if (integer > 0) {
            return true;
        } else {
            return false;
        }
    }


    /**
     *查询
     */
    @Override
    public Token getTokenByTokenId(String tokenId) {
        return baseMapper.selectOne(new QueryWrapper<Token>().lambda().eq(Token::getTokenId, tokenId));
    }
}
